from XmlParser import XmlParser, AllObjects
from ast import Tree
from Envs import Envs

import ply.lex as lex
import ply.yacc as yacc

debug = False

class QueryProcessor:
	def __init__(self):
		xmlParser = XmlParser()
		self.allObjects, self.ids = xmlParser.parseXml("xml/m0.xml")
		self.envs = Envs(self.allObjects.childrenList, self.ids)
		
		# statyczne przypisanie do Tree
		Tree.envs = self.envs
		Tree.ids = self.ids # wewnetrzne odnosniki niedostepne z zewnatrz
		
		lex.lex(module=self)
		yacc.yacc(module=self)
	
	################
	####  LEX  #####
	################
	reserved = {
		'where' : 'WHERE',
	}
	
	tokens = (
		'NAME','NUMBER',
		'PLUS','MINUS','TIMES','DIVIDE', 'MODULO', 'POWER',
		'LPAREN','RPAREN',
		'EQUALS','NOTEQUALS','LESS','MORE','LESSEQUAL','MOREEQUAL',
		'WHERE','DOT','COMMA',
		'QUOT', 'APOSTR',
		'ASSIGN'
	)
	
	def t_NAME(self, t):
		r'[a-zA-Z_][a-zA-Z_0-9]*'
		t.type = self.reserved.get(t.value,'NAME')    # Check for reserved words
		return t
	
	def t_NUMBER(self, t):
		r'\d+'
		try:
			t.value = int(t.value)
		except ValueError:
			print "Integer value too large", t.value
			t.value = 0
		return t
	
	t_PLUS		= r'\+'
	t_MINUS		= r'-'
	t_TIMES		= r'\*'
	t_DIVIDE	= r'/'
	t_MODULO	= r'%'
	t_POWER		= r'\*\*'
	t_LPAREN	= r'\('
	t_RPAREN	= r'\)'
	t_EQUALS	= r'=='
	t_NOTEQUALS	= r'!='
	t_LESS		= r'<'
	t_MORE		= r'>'
	t_LESSEQUAL	= r'<='
	t_MOREEQUAL	= r'>='
	
	def t_WHERE(self, t):
		r'(w|W)(h|H)(e|E)(r|R)(e|E)' # where obojetnie z jakich liter
		t.value = 'WHERE'
		return t
	
	t_DOT		= r'\.'
	t_COMMA		= r','
	t_QUOT		= r'\"'
	t_APOSTR	= r'\''
	t_ASSIGN	= r'='
	
	t_ignore = r' \t\r'
	
	def t_error(self, t):
		print "t_error", t
		t.lexer.skip(1)
	
	################
	####  YACC  ####
	################
	
	precedence = (
		('right','WHERE'),
		('left','ASSIGN'),
		('left','LESS','MORE','LESSEQUAL','MOREEQUAL','EQUALS','NOTEQUALS'),
		('left','PLUS','MINUS'),
		('left','TIMES','DIVIDE', 'MODULO'),
		('left', 'POWER'),
		('left','UMINUS','COMMA','DOT'),
	)
	
	def p_exit(self, p):
		'''statement : object
				| expr
				| string
				| condition
				| assign'''
		if debug: print "statement : object | expr | string", p[1]
		p[0] = p[1] 
	
	def p_object_dot(self, p):
		'''object : object DOT object'''
		if debug: print '''object : object DOT object''', p[1], p[3]
		dot = Tree("dot", None, p[1], p[3])
		p[0] = dot
		
	def p_object_comma(self, p):
		'''object : object COMMA object'''
		if debug: print '''object : object COMMA object''', p[1], p[3]
		comma = Tree("comma", None, p[1], p[3])
		p[0] = comma
	
	def p_object(self, p):
		'''object : NAME'''
		if debug: print '''object : NAME''', p[1]
		name = Tree("name", p[1])
		p[0] = name
	
	def p_object_relop_expr(self, p):
		'''condition : object relOp expr
				| object relOp string
				'''
		if debug: print '''condition : object relOp (expr | string)''', p[1], p[2], p[3]
		relop = Tree(p[2], None, p[1], p[3])
		p[0] = relop
		
	def p_object_relop_name(self, p):
		'''condition : object relOp NAME'''
		relop = Tree(p[2], None, p[1], Tree("name", p[3]))
		p[0] = relop
	
	def p_object_group(self, p):
		'''object : LPAREN object RPAREN'''
		if debug: print '''object : LPAREN object RPAREN''', p[2]
		p[0] = p[2]
		
	# problemy z gramatyka
	def p_object_binop(self, p):
		'''objectexpr : object binop object %prec PLUS
				| object binop expr %prec PLUS
				| expr binop object %prec PLUS'''
		binop = Tree(p[2], None, p[1], p[3])
		p[0] = binop
	
	
	
	def p_expr_objectexpr(self, p):
		'''expr : objectexpr'''
		p[0] = p[1]
	
	# koniec problemow z gramatyka
	
	def p_expr(self, p):
		'''expr : NUMBER'''
		if debug: print '''expr : NUMBER''', p[1]
		number = Tree("number", p[1])
		p[0] = number
		
	def p_expr_group(self, p):
		'''expr : LPAREN expr RPAREN'''
		if debug: print '''expr : LPAREN expr RPAREN''', p[2]
		p[0] = p[2]
	
	def p_relop(self, p):
		'''relOp : LESS
				| MORE
				| EQUALS
				| NOTEQUALS
				| LESSEQUAL
				| MOREEQUAL'''
		if debug: print '''relOp : ''', p[1]
		p[0] = p[1]
	
	def p_expr_relop(self, p):
		'''condition : expr relOp expr %prec EQUALS'''
		if debug: print '''condition : expr relOp expr %prec EQUALS''', p[1], p[2], p[3]
		relop = Tree(p[2], None, p[1], p[3])
		p[0] = relop
	
	def p_expr_binop(self, p):
		'''expr : expr binop expr %prec PLUS'''
		if debug: print '''expr : expr binop expr %prec PLUS''', p[1], p[2], p[3]
		binop = Tree(p[2], None, p[1], p[3])
		p[0] = binop
		
	def p_binop(self, p):
		'''binop : PLUS
				| MINUS
				| TIMES
				| DIVIDE
				| MODULO
				| POWER'''
		if debug: print '''binop : ''', p[1]
		p[0] = p[1]
		
	def p_string(self, p):
		'''string : QUOT NAME QUOT
				| APOSTR NAME APOSTR'''
#				| QUOT expr QUOT
#				| APOSTR expr APOSTR'''
		if debug: print "string : NAME", p[2]
		string = Tree("string", p[2])
		p[0] = string
	
	def p_where(self, p):
		'''object : object WHERE condition'''
		if debug: print '''object : object WHERE condition''', p[1], p[3]
		where = Tree("where", None, p[1], p[3])
		p[0] = where
	
	def p_assign(self, p):
		'''assign : NAME ASSIGN expr
				| NAME ASSIGN string'''
		assign = Tree("assign", None, Tree("name", p[1]), p[3])
		p[0] = assign
	
	def p_error(self, p):
		print "p_error", p
	
	##################
	####  PROCESS  ###
	##################
	def process(self, query):
		#"QUERY: ", 
		print query
		result = yacc.parse(query)
		printType, toPrint = result.eval()
		if printType == "objectList":
			for o in toPrint:
				print o
		elif printType == "nameList":
			printType, toPrint = Tree(printType, toPrint).eval()
			for o in toPrint:
				print o
		elif printType == "string" or printType == "bool" or printType == "number":
			print toPrint
		elif printType == "syntax":
			print "Syntax error"
		else:
			print "printType:", printType, "toPrint:", toPrint
		print 80*"-"

#########################
########  MAIN  #########
#########################
if __name__ == '__main__':
	queryProcessor = QueryProcessor()
	
	queryProcessor.process("osoba")
	queryProcessor.process("miasto")
	queryProcessor.process("osoba.imie")
	queryProcessor.process("osoba.(imie, nazwisko)")
	queryProcessor.process("miasto.(nazwa, wojewodztwo)")
	queryProcessor.process("(osoba, miasto)")
	queryProcessor.process("2**5")
	queryProcessor.process("2+2")
	queryProcessor.process("3+3*3")
	queryProcessor.process("(3+3)*3")
	queryProcessor.process("2>2")
	queryProcessor.process("21<2")
	queryProcessor.process("2>=2")
	queryProcessor.process("21<=2")
	queryProcessor.process("2==2")
	queryProcessor.process("2!=2")
	queryProcessor.process("2")
	queryProcessor.process("'miasto'")
	queryProcessor.process("b = 'Bydgoszcz'")
	queryProcessor.process("b")
	queryProcessor.process("miasto where nazwa == b")
	queryProcessor.process("(miasto where nazwa > 'Bydgoszcz').(nazwa, wojewodztwo)")
	queryProcessor.process("(osoba where wiek < 30).(imie, nazwisko, wiek)")
	queryProcessor.process("osoba where imie == 'Lukasz'")
	# TODO: sprobowac naprawic 2 ponizej
#	queryProcessor.process("osoba.adres.(\"ul. \"+ulica, miasto)")
	queryProcessor.process("a = 2")
	queryProcessor.process("b = 2+1")
	# 1) zmienne do operatorow
	queryProcessor.process("a+2")
	queryProcessor.process("a+b*2")
	queryProcessor.process("a+b")
#	queryProcessor.process("osoba.wiek + 2") # syntax error jeszcze nie wiem czemu pewnie not implemented
	queryProcessor.process("c = 'asdas'")
	queryProcessor.process("a = 2**5")
	queryProcessor.process("a ,b, c")
	# 2) podazanie za pointerami
	# brak chodzenia po pointerach !!!!!
#	queryProcessor.process("miasto where burmistrz.osoba.name == 'Lukasz'") # nested expection
	# 3) create
	# 4) delete
	# 5) z Main zrobic server oraz dopisac klienta
	
	# rozwalajace query
	


	# masta query
#	queryProcessor.process("(miasto where nazwa == 'Bydgoszcz').burmistrz.osoba.(imie, nazwisko)")